package main

/**
使用方法
	1、开一个命令行窗口，直接运行服务端，命令是：go run 122并发服务端.go
	2、编译客户端，命令是： go build 123并发客户端.go（已编译好的话，可以省略这一步）
	3、再开一个命令行窗口，运行客户端： ./123并发客户端
	4、在客户端中，输入任意字符，向服务端发送，就能收到响应。同时服务端也会有各种输出
	5、可以再开一个命令行窗口，模拟多个客户端，重复3、4步
**/

import (
	"fmt"
	"net"
	"strings"
)

//处理客户端的请求
func HandelConn(conn net.Conn) {
	defer conn.Close()

	//获取客户端的IP地址
	addr := conn.RemoteAddr().String()
	fmt.Println("新的连接:", addr)
	buf := make([]byte, 2048)

	//同一个连接进来，不一定只发一次数据。就像本例中这样，客户端连进来后，可以多次给服务器发消息。所以需要用个无限循环来处理
	//如果没有这个for，请求-响应一次后，服务端就会主动断开这个连接了
	i := 0
	for {
		i++
		fmt.Println(addr, " 同一个连接的请求计数：", i)
		n, err := conn.Read(buf) //这里会阻塞，所以不用担心会快速无限循环下去。换句话说，只有客户端发来一次消息，for循环中的代码块才会被执行一次
		if err != nil {
			fmt.Println("读取失败：", err)
			return
		}
		content := string(buf[:n])
		content = strings.Trim(content, "\n")
		fmt.Println(addr, " 客户端发来的数据是：【", content, "】,长度是：", n)
		if strings.Contains(content, "exit") {
			fmt.Println(addr, " 收到用户的退出请求，已断开连接")
			return
		}
		//响应给客户端
		conn.Write([]byte("我们收到了：" + content))
	}
}

func main() {
	//监听
	listen, err := net.Listen("tcp", "127.0.0.1:8888")
	defer listen.Close()
	if err != nil {
		fmt.Println("监听失败：", err)
		return
	}

	//如果没有for的话，不会等协程执行完，整个程序就结束了
	//即使用其它办法，让协程有机会运行。那么一个用户连接进来，并退出后，整个服务也就结束了
	//简单说，使用无限循环的目的就是能并发响应多个用户
	i := 0
	for {
		i++
		fmt.Println("阻塞，等待用户连接...")
		conn, err := listen.Accept() //如果此时没有了新用户连接进来，就会一直阻塞在这里，而不是无限的循环下去
		fmt.Println("新用户已连接，用户计数：", i)
		if err != nil {
			fmt.Println("监听失败:", err)
			return
		}
		//处理用户请求，每来一个用户就新建一个协程
		go HandelConn(conn)
	}
}
